// SPDX-License-Identifier: Mulan PSL v2
/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This software is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

use thiserror::Error;

use crate::ipc::bytewise::{
    BytewiseError, BytewiseReadOwned, BytewiseReader, BytewiseWrite, BytewiseWriter,
};

mod argument;
pub use argument::{Argument, ArgumentFlag};

mod request;
pub use request::Request;

mod response;
pub use response::Response;

pub mod macros;

mod consts {
    pub const MSG_TYPE_REQUEST: u8 = 0;
    pub const MSG_TYPE_RESPONSE: u8 = 1;
}

#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
pub enum MessageError {
    #[error("Request id mismatch (expect: {expect}, actual: {actual})")]
    RequestIdMismatch { expect: u64, actual: u64 },

    #[error("Argument count mismatch (expect: {expect}, actual: {actual})")]
    ArgumentCountMismatch { expect: usize, actual: usize },

    #[error("Argument storage mismatch")]
    ArgumentStorageMismatch,

    #[error("Argument type mismatch")]
    ArgumentTypeMismatch,

    #[error("Argument type size mismatch (expect: {expect}, actual: {actual})")]
    ArgumentTypeSizeMismatch { expect: usize, actual: usize },

    #[error("Argument type alignment mismatch (expect: {expect}, actual: {actual})")]
    ArgumentTypeAlignMismatch { expect: usize, actual: usize },

    #[error("Argument type length mismatch (expect: {expect}, actual: {actual})")]
    ArgumentTypeLengthMismatch { expect: usize, actual: usize },

    #[error("Attempted to downcast non-scalar argument to scalar")]
    ArgumentIsNotScalar,

    #[error("Attempted to downcast non-slice argument to slice")]
    ArgumentIsNotSlice,

    #[error("Attempted to access unaligned data")]
    UnalignedAccess,

    #[error("Attempted to access non mutable data as mutable")]
    IllegalMutation,

    #[error("Attempted to reference inlined data")]
    IllegalBorrowOfInlined,
}

#[derive(Debug, Clone)]
pub enum Message<'a> {
    Request(Request<'a>),
    Response(Response<'a>),
}

impl BytewiseReadOwned for Message<'_> {
    fn read_from<'a, R: BytewiseReader<'a>>(reader: &mut R) -> Result<Self, BytewiseError> {
        // Read msg type
        let msg_type = unsafe { *reader.read_ref::<u8>()? };

        // Read msg value
        match msg_type {
            consts::MSG_TYPE_REQUEST => {
                let request = Request::read_from(reader)?;
                Ok(Message::Request(request))
            }
            consts::MSG_TYPE_RESPONSE => {
                let response = Response::read_from(reader)?;
                Ok(Message::Response(response))
            }
            _ => panic!("Invalid message type: {}", msg_type),
        }
    }

    fn read_from_mut<'a, R: BytewiseReader<'a>>(reader: &mut R) -> Result<Self, BytewiseError> {
        // Read msg type
        let msg_type = unsafe { *reader.read_ref::<u8>()? };

        // Read msg value
        match msg_type {
            consts::MSG_TYPE_REQUEST => {
                let request = Request::read_from_mut(reader)?;
                Ok(Message::Request(request))
            }
            consts::MSG_TYPE_RESPONSE => {
                let response = Response::read_from_mut(reader)?;
                Ok(Message::Response(response))
            }
            _ => panic!("Invalid message type: {}", msg_type),
        }
    }
}

impl BytewiseWrite for Message<'_> {
    fn write_to<W: BytewiseWriter>(
        &self,
        writer: &mut W,
    ) -> Result<(), super::bytewise::BytewiseError> {
        match self {
            Message::Request(req) => {
                // Write msg type
                writer.write_ref(&consts::MSG_TYPE_REQUEST)?;

                // Write msg value
                req.write_to(writer)
            }
            Message::Response(resp) => {
                // Write msg type
                writer.write_ref(&consts::MSG_TYPE_RESPONSE)?;

                // Write msg value
                resp.write_to(writer)
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::ipc::bytewise::BytewiseBuffer;

    fn roundtrip_test(msg: &Message) {
        let mut buffer = [0u8; 512];

        let mut writer = BytewiseBuffer::new(&mut buffer);
        msg.write_to(&mut writer).expect("Failed to write message");
        println!("orig: {:#?}", msg);

        let mut reader = BytewiseBuffer::new(&buffer);
        let new_msg = Message::read_from(&mut reader).expect("Failed to read message");
        println!("new: {:#?}", new_msg);

        match (msg, new_msg) {
            (Message::Request(orig), Message::Request(new)) => {
                assert_eq!(orig.method_id(), new.method_id());
                assert_eq!(orig.request_id(), new.request_id());
                assert_eq!(orig.argc(), new.argc());
            }
            (Message::Response(orig), Message::Response(new)) => {
                assert_eq!(orig.method_id(), new.method_id());
                assert_eq!(orig.request_id(), new.request_id());
                assert_eq!(orig.argc(), new.argc());
            }
            _ => panic!("Message type mismatch"),
        }
    }

    #[test]
    fn test_message_roundtrip() {
        let request = Request::with_args(
            0xDEAD,
            vec![
                Argument::from_ref(&1u8, ArgumentFlag::ARG_IN),
                Argument::from_ref(&2i16, ArgumentFlag::ARG_OUT),
                Argument::from_ref(&4u64, ArgumentFlag::ARG_OUT),
            ],
        );

        let response = Response::with_request(&request, Argument::empty());

        roundtrip_test(&Message::Request(request));
        roundtrip_test(&Message::Response(response));
    }
}
